/*
 * QuadActor_RemoteRes.java
 *
 * Created on December 3, 2005, 1:57 PM
 */

package aa.application.AdaptiveQuad;

import aa.core.Actor;
import aa.core.ActorName;
import aa.core.CreateActorException;
import java.lang.Math;
import aa.core.ActorThread;

import aa.core.CommunicationException;

/**
 *
 * @author  xinghuizhao
 */
public class QuadActor_RemoteRes extends Actor{
    
    int numResponses; //number of response it has received if it is fib actor
    
    double partialResponse; 
    
    Double tolerance;
    
    ActorName facili;
  
    
    /** The first response we have received from a child fib actor. We save
     *  this value until we receive the second response, and send our client 
     *  the sum of both values 
     */
    
    ActorName client; //the actor which is suppose to receive result from this actor
    
    String meth; //method to invoke on teh receiving client (if this is a worker fib actor)
    
    String remoteIP; //ip address of the remote node
    
    ActorName remoteFaci; //the facilitator on the remote node
    
    /** Creates a new instance of QuadWorker */
    public QuadActor_RemoteRes(Double p_douTolerance,ActorName p_anFacili,String p_strIP) {
        tolerance = p_douTolerance;
        facili = p_anFacili;
        meth = "result";
        remoteIP = p_strIP;
        //remoteFaci = new ActorName("128.233.109.164",2);
        //remoteFaci = new ActorName("128.233.109.239",2);
        //remoteFaci = null;
    }
    
    public QuadActor_RemoteRes(Double p_douTolerance,ActorName p_anClient,ActorName p_anFacili, String p_strIP) {
        tolerance = p_douTolerance;
        facili = p_anFacili;
        client = p_anClient;
        meth = "result";
        remoteIP = p_strIP;
        //remoteFaci = new ActorName("128.233.109.164",2);
        //remoteFaci = new ActorName("128.233.109.239",2);
        //remoteFaci = null;
    }
    
    public QuadActor_RemoteRes(Double p_douTolerance,ActorName p_anClient,ActorName p_anFacili,ActorName p_anRemoteFaci) {
        tolerance = p_douTolerance;
        facili = p_anFacili;
        client = p_anClient;
        meth = "result";
        //remoteFaci = new ActorName("128.233.109.164",2);
        //remoteFaci = new ActorName("128.233.109.239",2);
        //remoteFaci = p_anRemoteFaci;
    }
    
    public QuadActor_RemoteRes(ActorName p_anClient,String p_strMeth,Double p_douTolerance,ActorName p_anFacili,String p_strIP) {
        client = p_anClient;
        facili = p_anFacili;
        meth = p_strMeth;
        numResponses = 0;
        tolerance = p_douTolerance;
        remoteIP = p_strIP;
        //remoteFaci = new ActorName("128.233.109.164",2);
        //remoteFaci = new ActorName("128.233.109.239",2);
        //remoteFaci = null;
    }
    
    public QuadActor_RemoteRes(ActorName p_anClient,String p_strMeth,Double p_douTolerance,String p_strIP) {
        client = p_anClient;
        meth = p_strMeth;
        numResponses = 0;
        tolerance = p_douTolerance;
        remoteIP = p_strIP;
        //remoteFaci = new ActorName("128.233.109.164",2);
        //remoteFaci = new ActorName("128.233.109.239",2);
        //remoteFaci = null;
       
    }
    
    public void setFaci(ActorName p_anNewFaci){
        facili = p_anNewFaci;
    }
    
    /** This method is called to do the quadrature computation
     * a: lower bound
     * b: upper bound
     * tolerance: the error tolerance
     * p_anClient: the actor which is suppose to receive result from this actor
     * p_strMeth: the name of the method to calculate result in client actor
     */

    
    public void quadrature(Double a,Double b,ActorName p_anClient,String p_strMeth) throws CommunicationException{
        
        double parameter = 2;
        
        double mid = (a.doubleValue()+b.doubleValue())/2;
        
        Double m = new Double(mid);
        
        double erroram = error(a,m);
        
        double errormb = error(m,b);
        
        double tol = tolerance.doubleValue();
        
        
        
        remoteFaci = (ActorName)call(facili,"findRemoteFaci",remoteIP);
        //System.out.println("RemoteFaci of "+ this +" --- "+remoteFaci);
        
        if ((erroram > tol)&&(errormb > tol)){ // need an actor for (a,m) and an actor for (m,b)
            
            //double percentam = erroram/(erroram + errormb);
            
            //int ipercentam = (int)percentam;
            
            
            
                
            if ((erroram > parameter*tol)&&(remoteFaci != null)){ //remote create actor
                    
                send(remoteFaci,"remoteCreateQuad",p_anClient,tolerance,a,m,getActorName().getLocationAddress());
                //System.out.println("send remoteCreatQuad "+m_athread.getActorName());
                    
            }
            else{
                
                try{
                    ActorName newChild=create("aa.application.AdaptiveQuad.QuadActor_RemoteRes",p_anClient,p_strMeth,tolerance,facili,remoteIP);
                
                
                
                    send(newChild,"quadrature",a,m,newChild,p_strMeth);
                
                
                    send(facili,"resourceAlloc",newChild,new Double(erroram));
                }catch (CreateActorException e){
                
                    System.out.println("Actor Creationi Exceptioni in creating "+ "QuadActor_RemoteRes "+e);
                
                }
            }
            
            
            if ((erroram < parameter*tol)&&(errormb > 2*tol)&&(remoteFaci !=null)){
                    
                send(remoteFaci,"remoteCreateQuad",p_anClient,tolerance,m,b,getActorName().getLocationAddress());
                //System.out.println("send remoteCreatQuad "+m_athread.getActorName());
            }
            else{
                
                try{
                    
                
                    ActorName newChild2=create("aa.application.AdaptiveQuad.QuadActor_RemoteRes",p_anClient,p_strMeth,tolerance,facili,remoteIP);
                
                
                
                    send(newChild2,"quadrature",m,b,newChild2,p_strMeth);
                
                    send(facili,"resourceAlloc",newChild2,new Double(errormb));              
                
                                                
                }catch (CreateActorException e){
                
                    System.out.println("Actor Creationi Exception in creating "+ "QuadActor_RemoteRes "+e);
                
                }
            }
        }
        
                
        if ((erroram > tol)&&(errormb < tol)){ //actor for (a,m)
            
            if ((erroram > parameter*tol)&&(remoteFaci != null)){
                send(remoteFaci,"remoteCreateQuad",p_anClient,tolerance,a,m,getActorName().getLocationAddress());
                //System.out.println("send remoteCreatQuad "+m_athread.getActorName());
            }
            else{
                try{
                    ActorName newChild=create("aa.application.AdaptiveQuad.QuadActor_RemoteRes",p_anClient,p_strMeth,tolerance,facili,remoteIP);
                
                
                
                    send(newChild,"quadrature",a,m,newChild,p_strMeth);
                
                    send(facili,"resourceAlloc",newChild,new Double(erroram));
                
                }catch (CreateActorException e){
                
                    System.out.println("Actor Creationi Exception in creating "+ "QuadActor_RemoteRes "+e);
                
                }
            }
                
            result(new Double(trapezoid(m,b)));
                
        }
            
        if ((erroram < tol)&&(errormb > tol)){ //actor for (m,b)
            
            
            result(new Double(trapezoid(a,m)));
            
            if ((errormb>2*tol)&&(remoteFaci!=null)) {
                send(remoteFaci,"remoteCreateQuad",p_anClient,tolerance,m,b,getActorName().getLocationAddress());
                //System.out.println("send remoteCreatQuad "+m_athread.getActorName());
            }
            else{   
                try{
                    
                    ActorName newChild3=create("aa.application.AdaptiveQuad.QuadActor_RemoteRes",p_anClient,p_strMeth,tolerance,facili,remoteIP);
                
                    
                
                    send(newChild3,"quadrature",m,b,newChild3,p_strMeth);
                    
                    send(facili,"resourceAlloc",newChild3,new Double(errormb));
                
                }catch (CreateActorException e){
                    System.out.println("Actor Creationi Exceptioni in creating "+ "QuadActor_RemoreRes "+e);
                }
            }
        }
                
        if ((erroram < tol)&&(errormb < tol)){ //no new actors
            
            result(new Double(trapezoid(a,m)));
                
            result(new Double(trapezoid(m,b)));
        
            
        }
        
        
    }
    
    /** The function */
    public double func(double x){
        //return x*x;
        if (x==0){
            //System.out.println("func 0");
            return 0;
        } else {
            double myFunc =  x*(Math.sin(1/x));
            //System.out.println("func:"+ myFunc);
            
            return myFunc;
        }
       
    }
    
    /** Calculate the area of a trapezoid */
    public double trapezoid(Double a,Double b){
        double aValue = a.doubleValue();
        double bValue = b.doubleValue();
        return (func(aValue)+func(bValue))*(bValue-aValue)/2;
    }
    
    /** Calculate the sum of areas of two sub trapezoids*/
    public double divideSum(Double a,Double b){
        double aValue = a.doubleValue();
        double bValue = b.doubleValue();
        double midpoint = (aValue+bValue)/2;
        Double dmid = new Double(midpoint);
        return (trapezoid(a,dmid)+trapezoid(dmid,b));
    }
    
    public double error(Double a, Double b){
        return Math.abs(trapezoid(a,b)-divideSum(a,b));
        
        
        //System.out.println((2*divideSum(a,b)-trapezoid(a,b))/3);
        //return ((2*divideSum(a,b)-trapezoid(a,b))/3);
    }
         
    /** This method is called from another child actor to pass a partial result.
     *  We wait until we have two such results, add them, and return the result to 
     *  client.
     *
     */
    
    public void result(Double p_douVal){
        if (numResponses==0){
            numResponses++;
            partialResponse = p_douVal.doubleValue();
        }
        else {
            //send the answer
            double myResult = p_douVal.doubleValue()+partialResponse;
            send(client,meth,new Double(myResult));
            
            System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" +myResult);
            System.out.flush();
            //destroy("Result actor no longer accessible, removing...");
            System.out.println("destoyed QuadActor_Res: "+ m_athread.getActorName());
            /*if (((ActorName)m_athread.getActorName()).getID()==4) {
                send(new ActorName("128.233.109.239",2),"printCurrentECash");
            }*/
        }
    }
    
}
